/**
 * @format
 * @Author: TC
 * @Date: 2023-02-26 21:52:33
 * @Last Modified by: TC
 * @Last Modified time: 2023-03-25 21:15:23
 */

import {
    Asset,
    assetManager,
    AudioClip,
    BufferAsset,
    EffectAsset,
    ImageAsset,
    JsonAsset,
    LabelAtlas,
    Material,
    Node,
    Prefab,
    Sprite,
    SpriteAtlas,
    SpriteFrame,
    Texture2D,
} from "cc";
import { app } from "../App";
import { BundleItem } from "../Module/Res/BundleItem";
import { ASSERT_TYPE } from "../Module/Res/ResConst";

export type OnErr = (err: Error) => void;
export type OnProgress = (complete: number, total: number) => void;
export type OnFinished = (res: Asset) => void;
/**
 * 加载管理
 */
export class ResManager {
    private _mapBundle: Map<string, BundleItem> = null;

    constructor() {
        this._mapBundle = new Map();
    }

    /**
     * 加载bundle
     * @param bundleName
     * @param cb
     */
    public loadBundle(bundleName: string, cb: (bundleItem: BundleItem) => void): void {
        let bundle = this._mapBundle.get(bundleName);
        if (bundle) {
            cb(bundle);
            return;
        }
        assetManager.loadBundle(bundleName, (err, bd) => {
            if (err) {
                app.log.error(err.message);
                return;
            }
            bundle = new BundleItem(bd);
            this._mapBundle.set(bundleName, bundle);
            cb(bundle);
        });
    }

    /**
     * 根据包名获取资源包
     * @param bundleName 包名
     * @returns
     */
    public getBundle(bundleName: string): BundleItem {
        const bundleItem = this._mapBundle.get(bundleName);
        if (!bundleItem) {
            app.log.error("get bundleItem err");
            return null;
        }
        return bundleItem;
    }

    /**
     * 加载资源
     * @param bundleName 包名
     * @param path 资源路径
     * @param type 资源类型
     * @param cb 回调
     */
    private _loadAsset(bundleName: string, path: string, type: ASSERT_TYPE, cb: (asset: Asset) => void): void {
        this.loadBundle(bundleName, (bundle: BundleItem) => {
            bundle.loadAsset(type, path, cb);
        });
    }

    /**
     * 加载文件夹
     * @param bundleName 包名
     * @param dir 文件夹路径
     * @param cb 回调
     */
    public loadDir(bundleName: string, dir: string, cb: () => void): void {
        this.loadBundle(bundleName, (bundle: BundleItem) => {
            bundle.loadDir(dir, cb);
        });
    }

    /**
     * 获取资源类型
     * @param type
     */
    public getCCAssetType(type: ASSERT_TYPE): typeof Asset {
        switch (type) {
            case ASSERT_TYPE.TEX_2D:
                return Texture2D;
            case ASSERT_TYPE.SPRITE_FRAME:
                return SpriteFrame;
            case ASSERT_TYPE.SPRITE_ATLAS:
                return SpriteAtlas;
            case ASSERT_TYPE.LABEL_ATLAS:
                return LabelAtlas;
            case ASSERT_TYPE.PREFAB:
                return Prefab;
            case ASSERT_TYPE.JSON:
                return JsonAsset;
            case ASSERT_TYPE.AUDIO:
                return AudioClip;
            case ASSERT_TYPE.MATERIAL:
                return Material;
            case ASSERT_TYPE.SHADER:
                return EffectAsset;
            case ASSERT_TYPE.BUFFER:
                return BufferAsset;
            case ASSERT_TYPE.OTHER:
                return Asset;
        }
    }

    public getAssetType(type: typeof Asset): ASSERT_TYPE {
        switch (type) {
            case Texture2D:
                return ASSERT_TYPE.TEX_2D;
            case SpriteFrame:
                return ASSERT_TYPE.SPRITE_FRAME;
            case SpriteAtlas:
                return ASSERT_TYPE.SPRITE_ATLAS;
            case LabelAtlas:
                return ASSERT_TYPE.LABEL_ATLAS;
            case Prefab:
                return ASSERT_TYPE.PREFAB;
            case JsonAsset:
                return ASSERT_TYPE.JSON;
            case AudioClip:
                return ASSERT_TYPE.AUDIO;
            case Material:
                return ASSERT_TYPE.MATERIAL;
            case EffectAsset:
                return ASSERT_TYPE.SHADER;
            case BufferAsset:
                return ASSERT_TYPE.BUFFER;
            default:
                return ASSERT_TYPE.OTHER;
        }
    }

    /**
     * 加载任意类型资源
     * @param bunleName
     * @param path
     * @param cb
     */
    public loadAny(bunleName: string, path: string, cb: (asset: Asset) => void): void {
        this._loadAsset(bunleName, path, ASSERT_TYPE.OTHER, (pfb: Asset) => {
            cb(pfb);
        });
    }
    /**
     * 加载二进制资源
     * @param bunleName
     * @param path
     * @param cb
     */
    public loadBin(bunleName: string, path: string, cb: (asset: BufferAsset) => void): void {
        this._loadAsset(bunleName, path, ASSERT_TYPE.BUFFER, (bin: BufferAsset) => {
            cb(bin);
        });
    }
    /**
     * 加载预制体
     * @param bunleName
     * @param path
     * @param cb
     */
    public loadPrefab(bunleName: string, path: string, cb: (asset: Prefab) => void): void {
        this._loadAsset(bunleName, path, ASSERT_TYPE.PREFAB, (pfb: Prefab) => {
            cb(pfb);
        });
    }

    /**
     * 加载音频资源
     * @param bunleName
     * @param path
     * @param cb
     */
    public loadAudio(bunleName: string, path: string, cb: (asset: AudioClip) => void): void {
        this._loadAsset(bunleName, path, ASSERT_TYPE.AUDIO, (audio: AudioClip) => {
            cb(audio);
        });
    }

    /**
     * 加载远程图片
     * @param url 地址
     * @param node 节点(需要修改贴图时传入)
     * @param cb
     */
    private _mapRemoteTex: Map<string, Texture2D> = new Map();
    public loadRemoteTexture(url: string, node?: Node, cb?: (asset: Texture2D) => void): void {
        const tex = this._mapRemoteTex.get(url);
        const func = (texture: Texture2D) => {
            if (node && node.isValid) {
                const sp = node.getComponent(Sprite);
                if (sp) {
                    const sf = new SpriteFrame();
                    sf.texture = texture;
                    sp.spriteFrame = sf;
                }
            }
        };
        if (tex) {
            func(tex);
            if (cb) cb(tex);
        } else {
            assetManager.loadRemote<ImageAsset>(url, { ext: ".jpg" }, (error, imageAsset) => {
                if (error) {
                    app.log.error("图片加载失败", error.message);
                    return;
                }
                const texture = new Texture2D();
                texture.image = imageAsset;
                func(texture);
                if (cb) cb(texture);
            });
        }
    }
}
